const fs = require('fs-extra')
const nodeModulePath = './node_modules'

const getPkg = (api, dep) => fs.existsSync(api.resolve(`${nodeModulePath}/${dep}/package.json`)) ?
  fs.readJsonSync(api.resolve(`${nodeModulePath}/${dep}/package.json`))
  : null

const getExternals = (api, pluginOptions = {externals: []}) => {
  const whitelist = []

  const userExternals = pluginOptions.externals.filter(d => {
    if (d.match(/^!/)) {
      whitelist.push(d.replace(/^!/, ''))
      return false
    }
    return true
  })

  const {dependencies} = require(api.resolve('./package.json'))

  const externalsList = Object.keys(dependencies).reduce((depList, dep) => {
    try {
      const pkg = getPkg(api, dep)
      if (!pkg) {
        throw new Error(`Could not find a package.json for module ${dep}`)
      }
      const name = userExternals.find(name => new RegExp(`^${pkg.name}(/|$)`).test(name))
      const fields = ['main', 'module', 'jsnext:main', 'browser']
      if (
        whitelist.indexOf(dep) === -1 &&
        (!fields.some((field) => field in pkg) ||
          !!pkg.binary ||
          !!pkg.gypfile ||
          !!name)
      ) {
        depList.push(name || dep)
      }
    } catch (e) {
      console.log(e)
      depList.push(dep)
    }
    return depList
  }, [])

  const externals = {}
  externalsList.forEach(d => externals[d] = `require("${d}")`)
  return externals
}

const getPackages = (api, externals = {}) => {
  const packageList = []
  Object.keys(externals).forEach(dep => {
    const pkg = getPkg(api, dep)
    if (!pkg) {
      throw new Error(`Could not find a package.json for module ${dep}`)
    }
    if (pkg.dependencies) {
      packageList.push(...getPackages(api, pkg.dependencies))
    }
    packageList.push(dep)
  })
  return packageList
}

const linkPackages = (api, externals = {}, filter = []) => {
  const packageList = new Set(getPackages(api, externals))
  packageList.forEach(dep => {
    fs.copy(api.resolve(`${nodeModulePath}/${dep}`), api.resolve(`${api.service.projectOptions.outputDir}/node_modules/${dep}`), {
      // TODO copy native module？
      // filter: source => [/.js$/, /.json$/, /.node$/, /.dll$/, /.exe$/, ...filter].some(reg => reg.test(source))
    })
  })
}

const buildPackage = (api, options, pluginOptions, main) => {
  const dir = `${api.getCwd()}/${options.outputDir}`
  fs.ensureDirSync(dir)
  const {name, version, dependencies} = api.service.pkg
  Object.assign(pluginOptions, {dependencies, name, version, main})
  fs.writeJSONSync(`${dir}/package.json`, pluginOptions)
  process.argv[2] = options.outputDir
}

module.exports = {linkPackages, getExternals, buildPackage}